#include "Star1.h"
#include <exception>
#include <cfloat>
#include "CommonApps.h"
//C'tor
//Expectimax::Expectimax(SenetHeuristics& heuristic): m_heuristic(heuristic){}

void Star1::Init(const SenetPlayerExt* owner , int gameStickNum)
{
	m_gameStickNum = gameStickNum;
	m_probabilities.init(gameStickNum);
	m_heuristic.init(owner , m_probabilities); 
	
}

// Assignment operator
//Expectimax& Expectimax::operator =(const Expectimax& e)
//{
//	m_heuristic = e.m_heuristic;
//	m_gameStickNum = e.m_gameStickNum;
//	return *this;
//}


//calculate negamax
void Star1::negamax(
						Score& res ,
						SenetBoardDraft& board,
						int depth,
						const GameTimer& timer,
						bool checkTime,
						Definitions::PlayerColor currentPlayer,
						int stickNum /* the number of sticks that current player got */,
						NodeType nodeType,
						float alpha,
						float beta)
{
	if(checkTime && timer.isMoveTimePassed(GameTimer::NO_OVERHEAD))
	{
		throw Exception::TIME_OUT;
	}
	res.pieceToMove = -1;
	if(board.isTerminal() || depth == 0)
	{
		res.score = m_heuristic.score(board, currentPlayer);
	}
	else
	{
		//TODO: Debug:
		alpha = CommonApps::floatMax(alpha, m_heuristic.getLowerHeuristicBound());
		beta = CommonApps::floatMin(beta, m_heuristic.getUpperHeuristicBound());


		// generate moves
		vector<int> moves;
		board.generateMoves(moves, currentPlayer, stickNum);
		int movesSize = moves.size();
		if(movesSize == 0)
		{
			res.score = m_heuristic.score(board, currentPlayer);
			return;
		}

		// for each child
		for(int i = 0 ;	i < movesSize;	++i)
		{
			int startLoc = moves[i];
			int endLoc = startLoc + stickNum;

			//do move
			vector<SenetBoardDraftMove> movesPerformed;
			board.doMove(movesPerformed, startLoc, endLoc, currentPlayer);

			//score move
			Score val = chanceSearch(
				board , depth-1 ,
				timer, checkTime ,
				Definitions::getOppositePlayer(currentPlayer),
				i , CHANCE, CommonApps::getOppositeNodeType(nodeType), -beta, -alpha);
			val.score *= -1.0f;

			//undo move
			CommonApps::undoMoves(movesPerformed, board , currentPlayer);

			// update the current maximum score
			// the following is actually: current_max = max(current_max, val)
			if(val.score > alpha)
			{
				if (val.score >= beta)
				{
					//res.score = val.score;
					res.score = beta;
					res.pieceToMove = startLoc;
					return;
				}
				alpha = val.score;
				res.pieceToMove = startLoc;
			}
		}
		res.score = alpha;
	}	
}


//calculate value at chance nodes
void Star1::expectimax(Score& res, SenetBoardDraft &board, int depth, const GameTimer& timer, bool checkTime , 
						Definitions::PlayerColor nextPlayer, NodeType nextTurn, float alpha, float beta) throw (Exception)
{
	if(checkTime && timer.isMoveTimePassed(GameTimer::NO_OVERHEAD))
	{
		throw Exception::TIME_OUT;
	}
	if(board.isTerminal() || depth == 0)
	{
		res.pieceToMove = -1;
		res.score = m_heuristic.score(board, nextPlayer);
	}
	else
	{
		float vsum = 0.0f;
	
		// chance node: no piece is selected here
		res.pieceToMove = -1;

		// get L and U
		float L = m_heuristic.getLowerHeuristicBound();
		float U = m_heuristic.getUpperHeuristicBound();

		// init Yi,Xi
		float Yi = 1.0f; // this is Y0 - the remaining probability which we didn't check yet
		float Xi = 0.0f; // this is X1 - the (value * probability) we already checked

		// Star1 search phase
		// for-each chance successor
		for(int i = 0; i <= m_gameStickNum; ++i)
		{
			Yi = Yi - m_probabilities.getProbability(i);	// this is Yi+1

			float Ai = ((alpha - U*Yi - Xi) / m_probabilities.getProbability(i));
			float Bi = ((beta - L*Yi - Xi) / m_probabilities.getProbability(i));
			float Ax = CommonApps::floatMax(Ai, L);
			float Bx = CommonApps::floatMin(Bi, U);

			Score val = chanceSearch(
				board, depth-1, timer,
				checkTime, nextPlayer, i,
				nextTurn, DONT_CARE, Ax, Bx);
			vsum += (val.score * m_probabilities.getProbability(i));

			// check fail-soft
			if (val.score <= Ai)
			{
				vsum += (U * getProbabilityLeft(i));
				res.score = vsum;
				return;
			}
			if (val.score >= Bi)
			{
				vsum += (L * getProbabilityLeft(i));
				res.score = vsum;
				return;
			}

			// continue to next iteration
			Xi = Xi + m_probabilities.getProbability(i) * val.score;	// this is Xi+1

		}
		res.score = vsum;
	}	
}


//driver function
Score Star1::chanceSearch(SenetBoardDraft& board, int depth, const GameTimer& timer, bool checkTime ,
						Definitions::PlayerColor currentPlayer, int stickNum ,  NodeType nodeType , NodeType nextTurn,
						float alpha, float beta) throw(Exception)
{	
	Score res;
	if(nodeType == CHANCE)
	{
		expectimax(res , board, depth, timer, checkTime, currentPlayer, nextTurn, alpha, beta);
	}
	else
	{
		//minimax(res , board, depth, timer, checkTime, currentPlayer, stickNum , nodeType);
		negamax(res , board, depth, timer, checkTime, currentPlayer, stickNum , nodeType, alpha, beta);
	}
	return res;
	
}


//main function - init turn parameters and calculation
SenetMove Star1::scoreExpectimax(SenetBoardDraft& board, int depth, const GameTimer& timer , 
									  bool checkTime , Definitions::PlayerColor myColor, int stickNum) throw(Exception)
{	
	//calculate score
	Score  res =  chanceSearch(board, depth, timer, checkTime , myColor, stickNum, MAX , DONT_CARE, -FLT_MAX, FLT_MAX);

	m_FinalBoardScore = res.score;

	SenetMove resMove(res.pieceToMove);	
	return resMove;

}
